package com.central.scheduler.remote.processor;

import com.central.scheduler.remote.NettyRemotingClient;
import com.central.scheduler.remote.command.Command;
import com.central.scheduler.remote.config.NettyClientConfig;
import com.central.scheduler.remote.utils.Host;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Tindy
 * @date 2021/11/3
 * @describe
 * task callback service
 */
@Service
public class StateEventCallbackService {

    private final Logger logger = LoggerFactory.getLogger(StateEventCallbackService.class);
    private static final int[] RETRY_BACKOFF = {1, 2, 3, 5, 10, 20, 40, 100, 100, 100, 100, 200, 200, 200};
    /**
     * remote channels
     */
    private static final ConcurrentHashMap<String, NettyRemoteChannel> REMOTE_CHANNELS = new ConcurrentHashMap<>();

    /**
     * netty remoting client
     */
    private final NettyRemotingClient nettyRemotingClient;

    public StateEventCallbackService() {
        final NettyClientConfig clientConfig = new NettyClientConfig();
        this.nettyRemotingClient = new NettyRemotingClient(clientConfig);
    }

    /**
     * send result
     *
     * @param command command
     */
    public void sendResult(String address, int port, Command command) {
        logger.info("send result, host:{}, command:{}", address, command.toString());
        Host host = new Host(address, port);
        NettyRemoteChannel nettyRemoteChannel = newRemoteChannel(host);
        if (nettyRemoteChannel != null) {
            nettyRemoteChannel.writeAndFlush(command);
        }
    }
    /**
     * get callback channel
     *
     * @param host
     * @return callback channel
     */
    private NettyRemoteChannel newRemoteChannel(Host host) {
        Channel newChannel;
        NettyRemoteChannel nettyRemoteChannel = REMOTE_CHANNELS.get(host.getAddress());
        if (nettyRemoteChannel != null) {
            if (nettyRemoteChannel.isActive()) {
                return nettyRemoteChannel;
            }
        }
        newChannel = nettyRemotingClient.getChannel(host);
        if (newChannel != null) {
            return newRemoteChannel(newChannel, host.getAddress());
        }
        return null;
    }
    private NettyRemoteChannel newRemoteChannel(Channel newChannel, String host) {
        NettyRemoteChannel remoteChannel = new NettyRemoteChannel(newChannel);
        addRemoteChannel(host, remoteChannel);
        return remoteChannel;
    }
    /**
     * add callback channel
     *
     * @param channel channel
     */
    public void addRemoteChannel(String host, NettyRemoteChannel channel) {
        REMOTE_CHANNELS.put(host, channel);
    }
}
